﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Configuration;
using System.Web.Mvc;
using System.Web.Security;
using System.Xml;
using Xproject.Util;
using Core.WeiXin;
using Services.WeiXinProject;
using Model.WeiXin;
using System.Threading.Tasks;
using Xproject.Framework.Core.DataContext;
using Repository.WeiXinProject;
using Model.Const;
using Model.WXshopBuiness;

namespace WeiXinProject.Controllers
{
    public class WXSignController : CommonBaseController
    {
        UserInfoServices m_UserInfoServices;
        WeiXinConfigServices m_weiXinConfigServices;
        WeiXinMessageServices m_wxmessageServices;
        public WXSignController(UserInfoServices mUserInfoServices, WeiXinConfigServices weiXinConfigServices, WeiXinMessageServices mwxmessageServices)
        {
            m_UserInfoServices = mUserInfoServices;
            m_weiXinConfigServices = weiXinConfigServices;
            m_wxmessageServices = mwxmessageServices;
        }

        [HttpGet]
        [ActionName("wxservice")]
        public ActionResult Get(string signature, string timestamp, string nonce, string echostr)
        {
            string token = "WeiXinProject";
            var ent = "";
            LogHelper.WriteInfoLog("signature " + signature + " timestamp: " + timestamp + " nonce: " + nonce + " echostr:" + echostr);
            if (CheckSignature(signature, timestamp, nonce, token, out ent))
            {
                return Content(echostr); //返回随机字符串则表示验证通过
            }
            return Content("参数错误！");
        }


        [HttpPost]
        [ActionName("wxservice")]
        public ActionResult Post()
        {
            Stream requestStream = System.Web.HttpContext.Current.Request.InputStream;
            byte[] requestByte = new byte[requestStream.Length];
            requestStream.Read(requestByte, 0, (int)requestStream.Length);
            string requestStr = Encoding.UTF8.GetString(requestByte);

            LogHelper.WriteInfoLog("微信服务器 请求值:" + requestStr);
            string resp = ProcMsg(requestStr);
            return Content(resp);          
        }
        /// <summary>
        /// 处理腾讯推送过来的消息
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string ProcMsg(string xml)
        {
            try
            {
                var result = "";
                var xmlDom = new XmlDocument();
                xmlDom.LoadXml(xml);
                var msgTypeXml =xmlDom.DocumentElement.SelectSingleNode("MsgType");
                if (msgTypeXml != null)
                {
                    string msgType = msgTypeXml.InnerText.ToLower();
                    switch (msgType) {
                        case "event":
                            result =ResponseEvent(xmlDom, xml);
                            break;
                        case "text"://文本消息
                            result = ResponseText(xmlDom, xml);
                            break;
                        case "voice"://语音消息
                            result = ResponseVoice(xmlDom, xml);
                            break;
                        case "image"://图片消息
                            result = ResponseImage(xmlDom, xml);
                            break;
                        case "video"://视频消息
                            result = ResponseVideo(xmlDom, xml);
                            break;
                        case "shortvideo"://小视频消息
                            result = ResponseShortVideo(xmlDom, xml);
                            break;
                        case "location"://位置消息
                            result = Responselocation(xmlDom, xml);
                            break;
                        case "link"://链接消息
                            result = ResponseLink(xmlDom, xml);
                            break;
                        default: result = "";break;
                    }
                }                
                return result;
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorLog("处理腾讯推送过来的消息 出现错误" + ex.ToString(), ex);
                return "";

            }
        }
        /// <summary>
        /// 响应消息类型为text文本的消息
        /// </summary>
        /// <param name="XmlDocument"></param>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string ResponseText(XmlDocument XmlDocument, string xml)
        {
            return  TransferMsgToWeiXinKF(XmlDocument, xml);
        }

        /// <summary>
        /// 响应消息类型为Voice文本的消息
        /// </summary>
        /// <param name="XmlDocument"></param>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string ResponseVoice(XmlDocument XmlDocument, string xml)
        {
            return TransferMsgToWeiXinKF(XmlDocument, xml);
        }
        /// <summary>
        /// 响应消息类型为Image文本的消息
        /// </summary>
        /// <param name="XmlDocument"></param>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string ResponseImage(XmlDocument XmlDocument, string xml)
        {
            return TransferMsgToWeiXinKF(XmlDocument, xml);
        }
        /// <summary>
        /// 响应消息类型为Video文本的消息
        /// </summary>
        /// <param name="XmlDocument"></param>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string ResponseVideo(XmlDocument XmlDocument, string xml)
        {
            return TransferMsgToWeiXinKF(XmlDocument, xml);
        }

        /// <summary>
        /// 响应消息类型为shortVideo文本的消息
        /// </summary>
        /// <param name="XmlDocument"></param>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string ResponseShortVideo(XmlDocument XmlDocument, string xml)
        {
            return TransferMsgToWeiXinKF(XmlDocument, xml);
        }
        /// <summary>
        /// 响应消息类型为location文本的消息
        /// </summary>
        /// <param name="XmlDocument"></param>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string Responselocation(XmlDocument XmlDocument, string xml)
        {
            return TransferMsgToWeiXinKF(XmlDocument, xml);
        }
        /// <summary>
        /// 响应消息类型为Link文本的消息
        /// </summary>
        /// <param name="XmlDocument"></param>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string ResponseLink(XmlDocument XmlDocument, string xml)
        {
            return TransferMsgToWeiXinKF(XmlDocument, xml);
        }
        /// <summary>
        /// 将消息转发的客服系统
        /// </summary>
        /// <param name="XmlDocument"></param>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string TransferMsgToWeiXinKF(XmlDocument xmlDom, string xml)
        {            
           //推送来的消息 fromUserName是 粉丝 也就是我们要求转发给客服的tousername
            var toUserNameNode = xmlDom.DocumentElement.SelectSingleNode("FromUserName");
            var toUserName = "";
            if (toUserNameNode != null)
                toUserName = toUserNameNode.InnerText;
            var fromUserNameNode = xmlDom.DocumentElement.SelectSingleNode("ToUserName");
            var fromUserName = "";
            if (fromUserNameNode != null)
                fromUserName = fromUserNameNode.InnerText;
            var createTimeNode = xmlDom.DocumentElement.SelectSingleNode("CreateTime");
            var createTime = "";
            if (createTimeNode != null)
                createTime = createTimeNode.InnerText;                           
         
            string  resultXml = @"<xml>
                                 <ToUserName><![CDATA[{0}]]></ToUserName>
                                 <FromUserName><![CDATA[{1}]]></FromUserName>
                                 <CreateTime>{2}</CreateTime>
                                 <MsgType><![CDATA[transfer_customer_service]]></MsgType>                                 
                             </xml>";
            LogHelper.WriteInfoLog("转发微信消息给客服 "+resultXml);
            resultXml = string.Format(resultXml,toUserName,fromUserName,createTime);
            return resultXml;
        }
        /// <summary>
        /// 响应消息类型为event的消息
        /// </summary>
        /// <param name="XmlDocument"></param>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string ResponseEvent(XmlDocument XmlDocument,string xml)
        {
            var node = XmlDocument.DocumentElement.SelectSingleNode("Event");
            string result = "";
            if (node == null) //如果没有事件 返回空给腾讯
                return "";
            else
            {
                switch (node.InnerText.ToLower())
                {
                    case "subscribe":
                        result = ProcSubscribeEvent(xml);
                        break;
                    case "unsubscribe":
                        result = ProcUnSubscribeEvent(xml);
                        break;
                    case "click":
                        result = ResponseClickEvent(xml);
                        break;
                    case "LOCATION"://上报地理位置事件
                        result = ResponseLOCATIONEvent(xml);
                        break;
                    default:                        
                        break;
                }
                return result;
            }
        }

        private string ResponseLOCATIONEvent(string Xml)
        {
            return "";
        }

     
        /// <summary>
        /// 关注公众号事件
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string ProcSubscribeEvent(string xml)
        {
            try
            {
                var xmlDom = new XmlDocument();
                xmlDom.LoadXml(xml);
                var node = xmlDom.DocumentElement.SelectSingleNode("Event");
                var toUserNameNode = xmlDom.DocumentElement.SelectSingleNode("ToUserName");
                var toUserName = "";
                if (toUserNameNode != null)
                    toUserName = toUserNameNode.InnerText;
                var fromUserNameNode = xmlDom.DocumentElement.SelectSingleNode("FromUserName");
                var fromUserName = "";
                if (fromUserNameNode != null)
                    fromUserName = fromUserNameNode.InnerText;
                var createTimeNode = xmlDom.DocumentElement.SelectSingleNode("CreateTime");
                var createTime = "";
                if (createTimeNode != null)
                    createTime = createTimeNode.InnerText;
                var eventKeyNode = xmlDom.DocumentElement.SelectSingleNode("EventKey");
                var eventKey = "";
                if (eventKeyNode != null)
                    eventKey = eventKeyNode.InnerText;
                var strParams ="";
                if (!string.IsNullOrWhiteSpace(eventKey) && eventKey.IndexOf("_", StringComparison.Ordinal) > 0)
                    strParams = eventKey.Split('_')[1];
                var ticket = xmlDom.DocumentElement.SelectSingleNode("Ticket");
                var strContent = xml + "\r\n" + "二维码参数:" + strParams;
                LogHelper.WriteInfoLog("微信服务器 请求值:" + strContent);              
                var resultXml = @"<xml>
                            <ToUserName><![CDATA[{0}]]></ToUserName>
                            <FromUserName><![CDATA[{1}]]></FromUserName>
                            <CreateTime>{2}</CreateTime>
                            <MsgType><![CDATA[text]]></MsgType>
                            <Content><![CDATA[{3}]]></Content>
                            <FuncFlag>{4}</FuncFlag>
                            </xml>";
                //在这儿做消息的排重 ，因为如果5秒内没有回复微信，微信会重新发送一个用户关注事件过来，会造成 插入三笔记录
                string openid =System.Web.HttpRuntime.Cache["subscribe" + fromUserName] as string;
                if (string.IsNullOrEmpty(openid)) //第一次通知肯定为空
                {
                    //存贮
                    System.Web.HttpRuntime.Cache.Insert("subscribe" + fromUserName, fromUserName, null, DateTime.Now.AddMinutes(1), System.Web.Caching.Cache.NoSlidingExpiration);
                }
                else {
                    //到这里说明微信服务器又发送了关注事件通知过来了，而我们程序没搞完事，还在干添加用户的事
                    return ""; //假如服务器无法保证在五秒内处理并回复，可以直接回复空串，微信服务器不会对此作任何处理，并且不会发起重试。
                }
               
                WeiXinUser u = m_UserInfoServices.GetWeiXinUserByOpenId(fromUserName);                 
                if(u!=null&&!string.IsNullOrEmpty(u.UserID))
                    //这种情况会发生，在使用推荐链接 发给没注册的新用户后，我们能拿到用户头像和openid，也确实存了数据库了，
                    //但是如果他不主动去关注的话，公众号是没有这个粉丝的，但是我们系统确实存好了这个用户
                    //这个时候，我们需要弹出那个公众号二维码遮罩层，让用户主动关注，然后微信会发送 关注通知到这里，
                    //否则，公众号是不会出现在他的微信聊天列表中的
                {

                    WeiXinConfig wxc = Cache_WeiXinConfig.Get("Cache_WeiXinConfig_" + CommonHelper.GetAppSettings("CONFIGID"));
                    if (wxc == null)
                    {
                        wxc = m_weiXinConfigServices.GetAllWeiXinConfig().FirstOrDefault();
                        Cache_WeiXinConfig.Set(wxc, "Cache_WeiXinConfig_" + CommonHelper.GetAppSettings("CONFIGID"));
                    }

                    resultXml = string.Format(resultXml, fromUserName, toUserName, ConvertHelper.ConvertDateTimeInt(DateTime.Now)
                        , wxc.subscribeWeclome, 0);
                    u.IsFocus = 1;
                    u.FocusTime = DateTime.Now;
                    m_UserInfoServices.UpdateWeiXinUserInfo(u);
                    ZXUserInfoModel zxuser = m_UserInfoServices.GetWeiXinZXUserInfoByOpenId(u.OpenId);
                    this.SetSession(zxuser, u.OpenId);//重置登录信息
                }
                else
                {
                    // Task t = Task.Run(delegate { RegisterUser(fromUserName, strParams, toUserName); });
                    //这种是关注事件，所以，isforce=1
                    long jifeng = 10;
                    string jf = CommonHelper.GetAppSettings("REGIST_GIFT_JF");
                    long.TryParse(jf, out jifeng);
                    RegisterUser(fromUserName, strParams, toUserName,1, jifeng);
                    WeiXinConfig wxc = Cache_WeiXinConfig.Get("Cache_WeiXinConfig_" + CommonHelper.GetAppSettings("CONFIGID"));
                    if (wxc == null)
                    {
                        wxc = m_weiXinConfigServices.GetAllWeiXinConfig().FirstOrDefault();
                        Cache_WeiXinConfig.Set(wxc, "Cache_WeiXinConfig_" + CommonHelper.GetAppSettings("CONFIGID"));
                    }
                    resultXml = string.Format(resultXml, fromUserName, toUserName, ConvertHelper.ConvertDateTimeInt(DateTime.Now)
                    , wxc.subscribeWeclome, 0);                   
                }
               
                return resultXml;
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorLog("关注事件异常：" + ex.Message + ex.StackTrace + ex.Source,ex);                
                return "";
            }
        }
        /// <summary>
        /// 取消关注事件
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        private string ProcUnSubscribeEvent(string xml)
        {
            try
            {
                var xmlDom = new XmlDocument();
                xmlDom.LoadXml(xml);
                var node = xmlDom.DocumentElement.SelectSingleNode("Event");
                var toUserNameNode = xmlDom.DocumentElement.SelectSingleNode("ToUserName");
                var toUserName = "";
                if (toUserNameNode != null)
                    toUserName = toUserNameNode.InnerText;
                var fromUserNameNode = xmlDom.DocumentElement.SelectSingleNode("FromUserName");
                var fromUserName = "";
                if (fromUserNameNode != null)
                    fromUserName = fromUserNameNode.InnerText;
                var createTimeNode = xmlDom.DocumentElement.SelectSingleNode("CreateTime");
                var createTime = "";
                if (createTimeNode != null)
                    createTime = createTimeNode.InnerText;
                var eventKeyNode = xmlDom.DocumentElement.SelectSingleNode("EventKey");
                var eventKey = "";
                if (eventKeyNode != null)
                    eventKey = eventKeyNode.InnerText;                              

                m_UserInfoServices.UpdateUserSubscribeState("", fromUserName, 0);

                LogHelper.WriteInfoLog("取消关注事件 "+fromUserName);
                ZXUserInfoModel zxuser = m_UserInfoServices.GetWeiXinZXUserInfoByOpenId(fromUserName);
                this.SetSession(zxuser, fromUserName);//重置登录信息
                return "";
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorLog("关注事件异常：" + ex.Message + ex.StackTrace + ex.Source, ex);              
                return "";
            }
        }

        /// <summary>
        /// 微信菜单上事件点击
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        private  string ResponseClickEvent(string xml)
        {
            var xmlDom = new XmlDocument();
            xmlDom.LoadXml(xml);
            var node = xmlDom.DocumentElement.SelectSingleNode("Event");
            var toUserNameNode = xmlDom.DocumentElement.SelectSingleNode("ToUserName");
            var toUserName = "";
            if (toUserNameNode != null)
                toUserName = toUserNameNode.InnerText;
            var fromUserNameNode = xmlDom.DocumentElement.SelectSingleNode("FromUserName");
            var fromUserName = "";
            if (fromUserNameNode != null)
                fromUserName = fromUserNameNode.InnerText;
            var createTimeNode = xmlDom.DocumentElement.SelectSingleNode("CreateTime");
            var createTime = "";
            if (createTimeNode != null)
                createTime = createTimeNode.InnerText;
            var eventKeyNode = xmlDom.DocumentElement.SelectSingleNode("EventKey");
            var eventKey = "";
            if (eventKeyNode != null)
                eventKey = eventKeyNode.InnerText;

            var strContent = "ResponseClickEvent:" + xml + "\r\n";
            LogHelper.WriteInfoLog("微信服务器 ResponseClickEvent请求值:" + strContent);            

            string resultXml = "";
            switch (eventKey)
            {
                case "WeiXin_GetQRCode_1":                
                case "WeiXin_GetQRCode_2":
                    resultXml = CreateQrCode(fromUserName, toUserName);
                    break;
            }
            return resultXml;
        }

        private  string CreateQrCode(string fromUserName, string toUserName)
        {
            string resultXml = @"
                            <xml>
                            <ToUserName><![CDATA[{0}]]></ToUserName>
                            <FromUserName><![CDATA[{1}]]></FromUserName>
                            <CreateTime>{2}</CreateTime>
                            <MsgType><![CDATA[text]]></MsgType>
                            <Content><![CDATA[{3}]]></Content>
                            </xml>";

            string errMsg = "";
            if (!string.IsNullOrEmpty(WebConfigurationManager.AppSettings["IsStopWebSite"]) && WebConfigurationManager.AppSettings["IsStopWebSite"] == "true")
            {
                errMsg = "公众号已迁移，请前往新公众号中生成名片，感谢您的配合，谢谢！";
                resultXml = string.Format(resultXml, fromUserName, toUserName, ConvertHelper.ConvertDateTimeInt(DateTime.Now).ToString(), errMsg);
                return resultXml;
            }                     
            try
            {
                WeiXinUser user = m_UserInfoServices.GetWeiXinUserByOpenId(fromUserName);
                string UserID = user.UserID;
                string NickName = user.NickName;
                string HeadUrl = user.HeadUrl;
                string filePath =  Server.MapPath("~/Content/Images/QRCode/" + UserID + ".jpg");             
                string imageId = WXcommonHelper.uploadImage2(UserID, NickName, HeadUrl);
                if (imageId == "xxx" && System.IO.File.Exists(filePath))
                {
                    errMsg = "您已生成过二维码名片！";
                    resultXml = string.Format(resultXml, fromUserName, toUserName, ConvertHelper.ConvertDateTimeInt(DateTime.Now).ToString(), errMsg);
                    return resultXml;
                }
                resultXml = string.Format(@"<xml>
                        <ToUserName><![CDATA[{0}]]></ToUserName>
                        <FromUserName><![CDATA[{1}]]></FromUserName>
                        <CreateTime>{2}</CreateTime>
                        <MsgType><![CDATA[image]]></MsgType>
                        <Image>
                        <MediaId><![CDATA[{3}]]></MediaId>
                        </Image>
                        </xml>", fromUserName, toUserName, ConvertHelper.ConvertDateTimeInt(DateTime.Now).ToString(), imageId);
            }
            catch (Exception ex)
            {
                errMsg = "名片生成失败！";
                resultXml = string.Format(resultXml, fromUserName, toUserName, ConvertHelper.ConvertDateTimeInt(DateTime.Now).ToString()
                            , errMsg);
                LogHelper.WriteInfoLog("CreateQrCode2 uploadImage2 Exception:" + ex.Message + "\r\n");            
            }

            return resultXml;
        }

        /// <summary>
        /// 微信校验
        /// </summary>
        /// <param name="signature"></param>
        /// <param name="timestamp"></param>
        /// <param name="nonce"></param>
        /// <param name="token"></param>
        /// <param name="ent"></param>
        /// <returns></returns>
        private bool CheckSignature(string signature, string timestamp, string nonce, string token, out string sign)
        {
            var arr = new[] { token, timestamp, nonce }.OrderBy(z => z).ToArray();
            var arrString = string.Join("", arr);
            sign= CommonHelper.SHA1(arrString);
            return signature == sign.ToLower();
        }


        private void RegisterUser(string curUserOpenId, string parentId, string fromUserName,int isFocus,long jifeng)
        {

            WeiXinConfig wxc = Cache_WeiXinConfig.Get("Cache_WeiXinConfig_" + CommonHelper.GetAppSettings("CONFIGID"));
            WeiXinUser user = null;
            if (wxc == null)
            {
                wxc = m_weiXinConfigServices.GetAllWeiXinConfig().FirstOrDefault();
                Cache_WeiXinConfig.Set(wxc, "Cache_WeiXinConfig_" + CommonHelper.GetAppSettings("CONFIGID"));
            }
            string name = wxc.Name;
            WeiXinModel.UserInfoModel userInfo = WeiXinHelper.GetUserInfoByOpenId(curUserOpenId);
            string openId = userInfo.openid;
            string nickName = userInfo.nickname;
            string headUrl = userInfo.headimgurl;
            string area = userInfo.province + userInfo.city;
            string sex = userInfo.sex.ToString();
            string unionId = userInfo.unionid;
            if (string.IsNullOrEmpty(unionId)) unionId = openId;

            try
            {
                //  WeiXinUser user = m_UserInfoServices.InsertWeiXinUserInfo(nickName, openId, headUrl, area, sex, unionId, parentId);
                string zxparent = this.Request.QueryString["zxpID"];
                string zxusersource = this.Request.QueryString["usource"];                
                string zxPlatform = CommonHelper.GetAppSettings("MessageSender"); 
                int level = 0, zxus = 0;
                long lzp = 0;
                long.TryParse(zxparent, out lzp);
                if (string.IsNullOrEmpty(zxusersource))
                {
                     zxus = (int)EnumUserSource.USER_SELF;
                }
                else {
                    int.TryParse(zxusersource, out zxus);
                }

                user =  m_UserInfoServices.InserWeiXinUserInfo_ZX(nickName, openId, headUrl, area, sex, unionId, parentId, lzp, zxus, zxPlatform, level,0,isFocus, jifeng);
            }
            catch (Exception e)
            {
                LogHelper.WriteInfoLog("************非常抱歉注册失败" +e.ToString() +e.StackTrace);
            }                                               
            if (user!=null)
            {
                //to do 多线程处理
               // m_wxmessageServices.SendOrderUserRegisterMessage(CommonHelper.GetAppSettings("MessageSender"), "", user.UserID, user.OpenId,
               //    CommonHelper.GetAppSettings("Domain")+CommonHelper.GetAppSettings("Default_Site_IndexPage"), CommonHelper.GetAppSettings("WeiXin_APPID"), user.NickName);              
            }
        }

    }
}